package RMI.Cenoura;

import RMI.Server.RemotePrado;
import java.rmi.RemoteException;
import Common.Cenoura;
import Common.EstadoCenoura;
import java.util.Random;

public class CenouraRemote extends Cenoura implements Runnable{

    private RemotePrado prado;
    Random genSeed = new Random();

    public CenouraRemote (RemotePrado prado, int id, int Eg,int Ef,int Tf,int Et, int Smin, int Smax, int line, int column) {
        super( id, 0, Eg, Ef, Tf, Smin, Smax, line, column);
        this.prado=prado;
    }

    private void growing_a_bit () {
        if((this.getEnergia() + this.getEg()) > this.getEf())
            this.setEnergia(this.getEf());
        else
            this.setEnergia(this.getEnergia() + this.getEg());
        try {
            prado.informCenoura(this.getId(), this.getEnergia(), EstadoCenoura.Growing, this.getX(), this.getY());
        }
        catch(RemoteException ex) {}
    }

    private void germinating () {
        try {
            if(prado.canGerminate(this.getId(), this.getX(), this.getY())){
                this.setState(EstadoCenoura.Growing);
                prado.informCenoura(this.getId(), this.getEnergia(), this.getState(), this.getX(), this.getY());
                super.log.Log("Mudanca de estado da cenoura " + this.getId() + ": " + this.getState());
            }
        }
        catch(RemoteException ex) {}
    }

    private void flowering () {
        this.setState(EstadoCenoura.Flower);
        try {
            prado.informCenoura(this.getId(), this.getEnergia(), this.getState(), this.getX(), this.getY());
        }
        catch(RemoteException ex) {}
        super.log.Log("Mudanca de estado da cenoura " + this.getId() + ": " + this.getState());
    }

    public int wait_for_pollen (int current_Tf) {
        return current_Tf-1;
    }

    private void die () {
        try {
            if(prado.hasSeeds(this.getX(), this.getY()))
                this.setState(EstadoCenoura.Seed);
            else
                this.setState(EstadoCenoura.Dead);
            prado.informCenoura(this.getId(), 0, EstadoCenoura.Dead, this.getX(), this.getY());
            super.log.Log("Mudanca de estado da cenoura " + this.getId() + ": " + this.getState());
        }
        catch(RemoteException ex) {}
    }

    private int pollinization () {
        this.setState(EstadoCenoura.Seeding);
        try {
            prado.informCenoura(this.getId(), this.getEnergia(), this.getState(), this.getX(), this.getY());
        }
        catch(RemoteException ex) {}
        super.log.Log("Mudanca de estado da cenoura " + this.getId() + ": " + this.getState());
        return genSeed.nextInt(this.getSmax() + 1 - this.getSmin()) + this.getSmin();

    }

    private int seeding (int Nseeds) {
        Random chooser = new Random();
        int direction = chooser.nextInt(8);
        /*
         * direction:   0 1 2
         *              7   3
         *              6 5 4
         */
        try {
            switch(direction) {
                case 0: prado.plantSeed(this.getX() - 1, this.getY() - 1); break;
                case 1: prado.plantSeed(this.getX(), this.getY() - 1); break;
                case 2: prado.plantSeed(this.getX() + 1, this.getY() - 1); break;
                case 3: prado.plantSeed(this.getX() + 1, this.getY()); break;
                case 4: prado.plantSeed(this.getX() + 1, this.getY() + 1); break;
                case 5: prado.plantSeed(this.getX(), this.getY() + 1); break;
                case 6: prado.plantSeed(this.getX() - 1, this.getY() + 1); break;
                case 7: prado.plantSeed(this.getX() - 1, this.getY());
            }
        }
        catch(RemoteException ex) {}
        return Nseeds--;
    }

    private void die_by_oldness () {
        try {
            if(prado.hasSeeds(this.getX(), this.getY()))
                this.setState(EstadoCenoura.Seed);
            else
                this.setState(EstadoCenoura.Dead);
            prado.informCenoura(this.getId(), 0, EstadoCenoura.Dead, this.getX(), this.getY());
            super.log.Log("Mudanca de estado da cenoura " + this.getId() + ": " + this.getState());
        }
        catch(RemoteException ex) {}
    }

    private void throw_seeds_and_die () {
        try {
            if(prado.hasSeeds(this.getX(), this.getY()))
                this.setState(EstadoCenoura.Seed);
            else
                this.setState(EstadoCenoura.Dead);
            prado.informCenoura(this.getId(), 0, EstadoCenoura.Dead, this.getX(), this.getY());
            super.log.Log("Mudanca de estado da cenoura " + this.getId() + ": " + this.getState());
        }
        catch(RemoteException ex) {}
    }

    public void run () {
        Random gerador = new Random(this.getId() + 10);
        int flowerTime = 0;
        int numSeeds = 0;

        //State Machine
        while(this.getState() != EstadoCenoura.Terminated)
        {
            switch(this.getState())
            {
                case Seed:
                    this.germinating();
                    // As cenouras germinam e vão crescendo com growing a bit
                    break;
                case Growing:
                    try {
                        if (prado.hasCarrot(this.getX(), this.getY())) {
                            if(this.getEnergia()<this.getEf())
                                this.growing_a_bit ();
                            else {
                                this.flowering();
                                flowerTime = this.getTf();
                            }
                        }
                        else
                            this.die();
                    }
                    catch(RemoteException ex) {}
                    break;
                case Flower:
                    try {
                        if (prado.hasCarrot(this.getX(), this.getY())) {
                            if(flowerTime <= 0)
                                this.die_by_oldness();
                            else {
                                if(prado.hasPollen(this.getX(), this.getY()))
                                    numSeeds = this.pollinization();
                                else
                                {
                                    flowerTime = this.wait_for_pollen(flowerTime);
                                    super.log.Log("FlowerTime Cenoura " + this.getId() + ": " + flowerTime);
                                }
                            }
                        }
                        else
                            this.die();
                    }
                    catch(RemoteException ex) {}
                    break;
                case Seeding:
                    try {
                        if (prado.hasCarrot(this.getX(), this.getY())) {
                            if(numSeeds == 0)
                                this.throw_seeds_and_die();
                            else
                                numSeeds = this.seeding(numSeeds);
                        }
                        else
                            this.die();
                    }
                    catch(RemoteException ex) {}
                    break;
                case Dead:
                    try {
                        if(prado.hasSeeds(this.getX(), this.getY()))
                            this.setState(EstadoCenoura.Seed);
                    }
                    catch(RemoteException ex) {}
            }
            try {
                if(prado.getEndFlag()) {
                    this.setState(EstadoCenoura.Terminated);
                    prado.informCenoura(this.getId(), 0, EstadoCenoura.Terminated, this.getX(), this.getY());
                }
                Thread.sleep(gerador.nextInt(201) + 900);
            }
            catch(RemoteException ex) {}
            catch(InterruptedException ex) {
                //Report Exception --> print ex.getMessage()
                return;
            }
        }
    }

}

